/* 
 * File:   ParticleEmiter.h
 * Author: RedEyedKiller
 *
 * Created on 20 Μάϊος 2010, 3:09 μμ
 */

#ifndef _PARTICLEEMITER_H
#define	_PARTICLEEMITER_H

#include "../Vector2.h"
#include "../Drawable.h"
#include "../Circle.h"
#include "../RandLib.h"
#include "Particle.h"
#include "../Utilities.h"
#include "Force.h"
#include "../sdl/GLColor.h"

/**
 * The class which produces, stores and updates Particles
 */

using namespace Math;
class LevelMap;
namespace physicsSystem
{

namespace particleSystem
{


class Particle;

/**
 * This struct contains all data which are indentical for all emiters in a system.
 */
struct ParticleEmiterPrototype
{

    ParticleEmiterPrototype() : birthColor(gl::colors::WHITE), deathColor(gl::colors::WHITE)
    {

    }

    const ParticleEmiterPrototype& operator =(const ParticleEmiterPrototype & orig)
    {
        memcpy(this, &orig, sizeof (ParticleEmiterPrototype));
        return (*this);
    }

    Math::Vector2F spawnRadius;
    Math::Vector2F defaultRelPosition;

    Math::Vector2F birthScale;
    Math::Vector2F deathScale;

    gl::Color birthColor;
    gl::Color deathColor;

    float scaleVariation;

    float minInitialVelocityMagnitude;
    float maxInitialVelocityMagnitude;

    float minInitialForceMagnitude;
    float maxInitialForceMagnitude;

    float minMass;
    float maxMass;

    int minInitialVelocityAngle;
    int maxInitialVelocityAngle;

    int minInitialForceAngle;
    int maxInitialForceAngle;

    double lifetime;
    int maxParts;

    bool fullCircle;
    bool offscreen;
    bool updateStyle;
};

struct AbstractAliveCheck
{
    virtual bool operator()(Particle * particle) = 0;
};

struct BoundingShapeAliveCheck : public AbstractAliveCheck
{

    BoundingShapeAliveCheck(Math::Shape * shape)
    {
        boundingShape = shape;
    }

    Math::Shape* boundingShape;

    bool operator()(Particle * particle)
    {
        return boundingShape->Intersect(particle->GetPosition());
    }
};

struct TimedAliveCheck : public AbstractAliveCheck
{

    TimedAliveCheck(unsigned long lifetime)
    {
        this->lifetime = lifetime;
    }

    bool operator()(Particle * particle)
    {
        return particle->IsAlive(lifetime);
    }

    unsigned long lifetime;
};

struct TimedBoundingShapeAliveCheck : public AbstractAliveCheck
{

    TimedBoundingShapeAliveCheck(unsigned long lifetime, Math::Shape * shape)
    {
        boundingShape = shape;
        this->lifetime = lifetime;
    }

    bool operator()(Particle * particle)
    {
        return particle->IsAlive(lifetime) && boundingShape->Intersect(particle->GetPosition());
    }

    unsigned long lifetime;
    Math::Shape* boundingShape;

};

/**
 * A class that creates particles and determines their starting properties.
 * @param prot -  A prototype struct given by the ParticleSystem.
 * @param pos - The position of the emiter. If not provided a default position
 * of the prototype will be used.
 * @param id - The unique id of this emiter. It s also the position of this inside
 *  the emiters vector of parent ParticleSystem.
 */
class ParticleEmiter
{
public:
    /*Constructors and destructor.*/
    ParticleEmiter(ParticleEmiterPrototype* prot, const Math::Vector2F& pos, const Math::Vector2F& dir, int id);
    virtual ~ParticleEmiter();

    /**
     * This is the general update method. If clip is specified the ClipUpdate will
     * be used, otherwise TimeUpdate will be used.
     * @param clip The rect outside of which all particles are considered dead.
     * @return false if this emiter should be considered dead.
     */
    bool Update(float seconds, Rect* clip = NULL);

    /**
     * Applies the effect of the given force to all particles of this emiter.
     * @param force
     */
    void ApplyForce(forces::Force* force);

    /*
     * These blit each particle in target.Note that alive but deactivated emiters
     * will still blit their particles in order to produce the effect of death transition
     * of the emiter
     */
    void Render(sdl::GraphicsCore* gCore, const gl::Texture& rsc, const Math::Rect& clip, const Math::Rect& camera, gl::BlendingMethodName blending);
    void RenderWrapped(sdl::GraphicsCore* gCore, const gl::Texture& rsc, const Math::Rect& clip, const Math::Rect& camera, gl::BlendingMethodName blending);

    /**
     * Kill any particle that collides with a solid tile.
     */
    void TileCollisionCheck(LevelMap* map);
    void AddParticles(int threashold);
    void AddParticlesViaRecycle(int threashold);

    /*initialize the state of a particle*/
    void InitializeParticle(Particle* part);
    /*
     * if the emiter is dead ParticleSystem will resurect an emiter instead of
     * allocating a new one
     */
    void Resurect(const Vector2F& pos, const Vector2F& dir, int id, bool wipe/* = false*/);
    /*
     * the method to deactivate an emiter which will eventually die.
     * Note that an emiter can only be deactivated
     * and it will die(alive=false) only when all its particles are out of lifetime
     */
    void Kill();

    /*
     * Deletes all particles
     */
    void WipeData();
    //getters and setters
    void SetPosition(Math::Vector2F position);
    Math::Vector2F GetPosition() const;
    bool IsAlive();
    int getId();
    void setId(int id);
    void Move(float x, float y);
    void SetActive(bool active);
    bool IsActive() const;
    void SetAlive(bool alive);
    void SetAsRecycler(bool create);
    bool IsRecycler() const;
    void SetDirection(Math::Vector2F direction);
    Math::Vector2F GetDirection() const;
protected:

    /**
     * Used in recycle mode to track which particle is used as new.
     */
    std::list<Particle*>::iterator current;

    /**
     * The vector containing all particles created by this emiter.
     */
    typedef std::list<Particle*> ParticleList;
    ParticleList particles;

    /**
     * An instance to the common data which all emiters of the same ParticleSystem share.
     */
    ParticleEmiterPrototype* prototype;

    /**
     * The relative to the system position of the emiter.
     */
    Math::Vector2F position;

    /**
     * The direction this emmiter faces.
     */
    Math::Vector2F direction;

    /**
     * The shape (circle or rect) in which the emiter is allowed to have particles.
     * Particles outside this shape will be killed!
     * Leave it null if you don't want a restriction like this.
     */
    Math::Shape* boundingShape;

    /**
     * The number of particles this emiter has created.
     */
    int currentPart;

    /**
     * The int through which you can reference to this emiter via ParticleSystem.
     */
    int id;

    /**
     * If false emiter will cease to operate.
     */
    bool alive;

    /**
     * If false emiter will cease to produce or recycle particles until
     * all particles are dead in which case emiter wil also die.
     */
    bool active;

    /**
     * If true emiter will create more particles until it reaches the max permited number.
     */
    bool recycler;
};

};
};
#endif	/* _PARTICLEEMITER_H */
